#include "vector.h"
#include "object.h"
#include <iostream>
#include <GL/gl.h>
#include <cmath>

Object::Object(int _id, int _type, bool _primitive) : id(_id), type(_type), primitive(_primitive){
    selected = false;
}

Object::Object(const Material &_m, int _id, int _type, bool _primitive) : id(_id), type(_type), primitive(_primitive), material(_m){
    selected = false;
}

Object::Object(const Material &_m, const Vector3D &_c, int _id, int _type, bool _primitive) : id(_id), type(_type), primitive(_primitive), material(_m), center(_c){
    selected = false;
    transformation.addTranslation(center);
}

Object::Object(const Material &_m, const Vector3D &_c, int _id, int _type, bool _primitive, const Matrix &_matrix, const Matrix &_matrixI)
    : id(_id), type(_type), primitive(_primitive), material(_m), center(_c){
    selected = false;
    transformation.setMatrices(_matrix, _matrixI);
}

Object::Object(const Material &_m, const Vector3D &_c, int _id, int _type, bool _primitive, const Transformation &_trans)
    : id(_id), type(_type), primitive(_primitive), material(_m), center(_c), transformation(_trans){
    selected = false;
}

Object::~Object(){
}

int Object::getId() const{
    return id;
}

void Object::setId(int _id){
    id = _id;
}

const Material &Object::getMaterial() const{
    return material;
}

void Object::setMaterial(const Material &_m){
    material = _m;
}

const Vector3D &Object::getCenter() const{
    return center;
}

void Object::setCenter(const Vector3D &_c){
    Vector3D t = _c - center;
    transformation.addTranslation(t);
    center = _c;
//    relativePosition = relativePosition + (t);
}
//
//void Object::setRelativePosition(const Vector3D &_v){
//    relativePosition = _v;
//}

bool Object::isSelected() const{
    return selected;
}

void Object::select(bool _b){
    selected = _b;
}

bool Object::isPrimitive() const{
    return primitive;
}

int Object::getType() const{
    return type;
}

void Object::translate(const Vector3D &_t){
    transformation.addTranslation(_t);

    center = center + _t;
}

void Object::rotate(double _tetha, const Vector3D &_axis, const Vector3D &_relativePosition, bool _useRelativePos){
    Matrix m;
    if(_useRelativePos) m = transformation.addRotation(toRad(_tetha), _axis.normalize(), center, center - _relativePosition);
    else m = transformation.addRotation(toRad(_tetha), _axis.normalize(), center, center - center);

    center = (m * center.toHomogeneousCoordinates()).fromHomogeneousCoordinates();
}

void Object::scale(const Vector3D &_s, const Vector3D &_relativePosition, bool _useRelativePos){
    Matrix m;
    if(_useRelativePos) m = transformation.addScale(_s, center, center - _relativePosition);
    else m = transformation.addScale(_s, center, center - center);

    center = (m * center.toHomogeneousCoordinates()).fromHomogeneousCoordinates();
}

void Object::setBoxPoints(const Vector3D &_min, const Vector3D &_max, Vector3D *_point) const{
    _point[0] = _min;
    _point[1] = Vector3D(_max.x, _min.y, _min.z);
    _point[2] = Vector3D(_min.x, _min.y, _max.z);
    _point[3] = Vector3D(_max.x, _min.y, _max.z);
    _point[4] = Vector3D(_min.x, _max.y, _min.z);
    _point[5] = Vector3D(_max.x, _max.y, _min.z);
    _point[6] = Vector3D(_min.x, _max.y, _max.z);
    _point[7] = _max;
}

void Object::drawBox(bool _wireframe, const Vector3D &_min, const Vector3D &_max, bool _useParentColor, const Material &_parentColor) const{
    if(_wireframe){
//        glDisable(GL_LIGHT0);
//        glDisable(GL_LIGHTING);

        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        glDisable(GL_CULL_FACE);

//        if(!selected) glLineWidth(2);
    }

    Vector3D point[8];
    setBoxPoints(_min, _max, point);

	glPushMatrix();
	glMultMatrixd(transformation.getLocalToWorld().toGlMatrix());

    Vector3D normal;
    glBegin(GL_QUADS);
        Material color_ = chooseMaterial(_wireframe, _useParentColor, _parentColor);
        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color_.getAmbientGL());
        glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, color_.getDiffuseGL());
        glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, color_.getSpecularGL());

    //parte de baixo
        normal = ((point[4] - point[0]).crossProduct(point[1] - point[0])).normalize();
        glNormal3f(normal.x, normal.y, normal.z);
        glVertex3f(point[0].x, point[0].y, point[0].z);
        glVertex3f(point[4].x, point[4].y, point[4].z);
        glVertex3f(point[5].x, point[5].y, point[5].z);
        glVertex3f(point[1].x, point[1].y, point[1].z);


    //parte de cima
        glNormal3f(-normal.x, -normal.y, -normal.z);
        glVertex3f(point[2].x, point[2].y, point[2].z);
        glVertex3f(point[3].x, point[3].y, point[3].z);
        glVertex3f(point[7].x, point[7].y, point[7].z);
        glVertex3f(point[6].x, point[6].y, point[6].z);

    //parte de esquerda
        normal = ((point[2] - point[0]).crossProduct(point[4] - point[0])).normalize();
        glNormal3f(normal.x, normal.y, normal.z);
        glVertex3f(point[0].x, point[0].y, point[0].z);
        glVertex3f(point[2].x, point[2].y, point[2].z);
        glVertex3f(point[6].x, point[6].y, point[6].z);
        glVertex3f(point[4].x, point[4].y, point[4].z);

    //parte de direita
        glNormal3f(-normal.x, -normal.y, -normal.z);
        glVertex3f(point[1].x, point[1].y, point[1].z);
        glVertex3f(point[5].x, point[5].y, point[5].z);
        glVertex3f(point[7].x, point[7].y, point[7].z);
        glVertex3f(point[3].x, point[3].y, point[3].z);

    //parte de frente
        normal = ((point[1] - point[0]).crossProduct(point[2] - point[0])).normalize();
        glNormal3f(normal.x, normal.y, normal.z);
        glVertex3f(point[0].x, point[0].y, point[0].z);
        glVertex3f(point[1].x, point[1].y, point[1].z);
        glVertex3f(point[3].x, point[3].y, point[3].z);
        glVertex3f(point[2].x, point[2].y, point[2].z);

    //parte de tras
        glNormal3f(-normal.x, -normal.y, -normal.z);
        glVertex3f(point[4].x, point[4].y, point[4].z);
        glVertex3f(point[6].x, point[6].y, point[6].z);
        glVertex3f(point[7].x, point[7].y, point[7].z);
        glVertex3f(point[5].x, point[5].y, point[5].z);
    glEnd();

	glPopMatrix();

    if(_wireframe){
//        glLineWidth(1);

        glEnable(GL_LIGHT0);
        glEnable(GL_LIGHTING);

        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
        glEnable(GL_CULL_FACE);
    }
}

double Object::toRad(double _x) const{
    return (_x * M_PI)/180.0;
}

Material Object::chooseMaterial(bool _wireframe, bool _useParentColor, const Material &_parentColor) const{
    Material result;

    if(_useParentColor) result = _parentColor;
    else result = material;

    if(_wireframe && selected){
        double mean[3];
        for(int i = 0; i < 3; i++){
            mean[i] = result.getAmbient()[i];
            mean[i] += result.getDiffuse()[i];
            mean[i] += result.getSpecular()[i];
            mean[i] = mean[i] / 3.0;
        }
        if( (mean[0] >= 0.7) && (mean[1] <= 0.3) && (mean[2] <= 0.3) ) result = Material(1, 1, 1);
        else result = Material(1, 0, 0, 1, 0, 0, 1, 0, 0);
    }

    return result;
}

Vector3D Object::normal(const Vector3D &_p) const{
    std::cout <<"Usando a normal geral." <<std::endl;
    return _p;
}
